<?php

namespace library\Core\Db;

class CDbOperator
{


    public $_sqlBuilder = NULL;

    public function __construct()
    {
        $this->_sqlBuilder = new CDbSqlBuilder();
    }

    private function queryInternal($method, CDbCriteriaBuilder $criteriaBuilder, $isArray = false)
    {

        $masterTable = $criteriaBuilder->tableClass;
        $dbname = $masterTable->_DbName_;

        $adapter = 'slave';
        if (CDbMSController::$MasterOrSlave === NULL) {
            $adapter = $masterTable->_SeleteAdapter;
        } else {
            $adapter = CDbMSController::$MasterOrSlave;
        }

        $sqlBuilder = new CDbSqlBuilder();
        $sqlObject = $sqlBuilder->createFindCommand($criteriaBuilder);
        $connect = CDbFactory::factory($dbname, $adapter);

        if (!$isArray) {
            $connect->setFetchTableName(1);
        }

        $command = new CDbCommand($connect);
        $result = $command->$method($sqlObject->getSql(), $sqlObject->getBindVal());

        $connect->setFetchTableName(0);

        if ($isArray) {
            return $result;
        }

        $res = array();
        foreach ($result as $row) {
            $res[] = $this->transform($criteriaBuilder->table, $row);
        }
        return $res;
    }

    public function query(CDbCriteriaBuilder $criteriaBuilder, $isArray = false)
    {

        return $this->queryInternal('query', $criteriaBuilder, $isArray);
    }

    public function queryRow(CDbCriteriaBuilder $criteriaBuilder, $isArray = false)
    {

        return $this->queryInternal('queryRow', $criteriaBuilder, $isArray);
    }

    public function queryOne(CDbCriteriaBuilder $criteriaBuilder, $isArray = false)
    {

        return $this->queryInternal('queryOne', $criteriaBuilder, $isArray);
    }

    public function queryBySql(CDbSqlPrepare $prepare, $isArray = false)
    {

        $adapter = $prepare->adapter;

        if (CDbMSController::$MasterOrSlave !== NULL) {

            $adapter = CDbMSController::$MasterOrSlave;
        }

        $connect = CDbFactory::factory($prepare->dbName, $adapter);

        if (!$isArray) {
            $connect->setFetchTableName(1);
        }

        $command = new CDbCommand($connect);
        $result = $command->query($prepare->getSql(), $prepare->getBindVal());

        $connect->setFetchTableName(0);

        if ($isArray) {
            return $result;
        }

        $res = array();
        foreach ($result as $row) {
            $res[] = $this->transform($criteriaBuilder->table, $row);
        }
        return $res;
    }

    public function excuteSql(CDbSqlPrepare $prepare)
    {

        $adapter = $prepare->adapter;

        if (CDbMSController::$MasterOrSlave !== NULL) {

            $adapter = CDbMSController::$MasterOrSlave;
        }

        $connect = CDbFactory::factory($prepare->dbName, $adapter);

        $command = new CDbCommand($connect);

        return $command->execute($prepare->getSql(), $prepare->getBindVal());

    }

    public function insert(CDbCriteriaBuilder $criteriaBuilder, $adapter = 'master')
    {

        $masterTable = $criteriaBuilder->tableClass;
        $dbname = $masterTable->_DbName_;

        $sqlBuilder = new CDbSqlBuilder();
        $sqlObject = $sqlBuilder->createInsertCommand($criteriaBuilder);
        $connect = CDbFactory::factory($dbname, $adapter);
        $command = new CDbCommand($connect);
        $result = $command->execute($sqlObject->getSql(), $sqlObject->getBindVal());

        return $command->getLastInsertID();
    }

    public function update(CDbCriteriaBuilder $criteriaBuilder, $adapter = 'master')
    {

        $masterTable = $criteriaBuilder->tableClass;
        $dbname = $masterTable->_DbName_;

        $sqlBuilder = new CDbSqlBuilder();
        $sqlObject = $sqlBuilder->createUpdateCommand($criteriaBuilder);
        $connect = CDbFactory::factory($dbname, $adapter);
        $command = new CDbCommand($connect);
        $result = $command->execute($sqlObject->getSql(), $sqlObject->getBindVal());

        return $result;
    }


    public function delete(CDbCriteriaBuilder $criteriaBuilder, $adapter = 'master')
    {

        $masterTable = $criteriaBuilder->tableClass;
        $dbname = $masterTable->_DbName_;

        $sqlBuilder = new CDbSqlBuilder();
        $sqlObject = $sqlBuilder->createDeleteCommand($criteriaBuilder);
        $connect = CDbFactory::factory($dbname, $adapter);
        $command = new CDbCommand($connect);
        $result = $command->execute($sqlObject->getSql(), $sqlObject->getBindVal());

        return $result;
    }

    protected function createStatement(CDbSqlPrepare $prepare, $mode = array(PDO::FETCH_ASSOC))
    {

        $connect = CDbFactory::factory($prepare->dbName, $prepare->adapter);
        $command = new CDbCommand($connect);
        $statement = $command->createStatement($prepare->getSql(), $prepare->getBindVal(), $mode);

        return $statement;
    }


    public function beginTransaction($dbName, $adapter)
    {
        $connect = CDbFactory::factory($dbName, $adapter);
        return $connect->beginTransaction();
    }

    public function commit($dbName, $adapter)
    {
        $connect = CDbFactory::factory($dbName, $adapter);
        return $connect->commit();
    }

    public function rollback($dbName, $adapter)
    {
        $connect = CDbFactory::factory($dbName, $adapter);
        return $connect->rollback();
    }

    private function transform($table, $row)
    {

        $tableClass = new CDbTable();

        $newRow = array();
        foreach ($row as $k => $v) {
            $tableField = explode('.', $k);
            if ($tableField[0] == $table || empty($tableField[0])) {
                $tableClass->$tableField[1] = $v;
            } else {
                if (!isset($tableClass->$tableField[0])) $tableClass->$tableField[0] = new CDbTable();

                $tableClass->$tableField[0]->$tableField[1] = $v;
            }
            isset($newRow[$tableField[1]]) ? $newRow[$k] = $v : $newRow[$tableField[1]] = $v;
        }
        $tableClass->setOriginalRow($newRow);

        return $tableClass;

    }
}